/*
 * Copyright (C) 2012 POAS.VSTU
 */

package model;

import javax.swing.event.EventListenerList;
import java.util.List;
import java.util.ArrayList;
import java.util.Iterator;

/**
 * Неспециализированная стопка карт
 */
public class CardPile implements Iterable<Card> {

    /** Список слушателей событий */
    protected EventListenerList listenerList = new EventListenerList();

    /** Контейнер с картами */
    protected ArrayList<Card> mCards = new ArrayList<Card>();

    /** Контейнер выбранными картами */
    protected ArrayList<Card> mSelectedCards = new ArrayList<Card>();

    /**  Размер стопки карт */
    public int size() {
        return mCards.size();
    }

    /**
     * Достает верхнюю карту из стопки
     * @return карта из стопки
     */
    public Card pop() {

        //если в стопке нет карт, то и доставать нечего
        if(size() == 0) {
            return null;
        }

        int lastIndex = size()-1;
        Card card = mCards.get(lastIndex);
        mCards.remove(lastIndex);
        return card;
    }

    /**
     * Достает все карты из стопки
     * @return  карты из стопки
     */
    public ArrayList<Card> popAll() {

        ArrayList<Card> other = (ArrayList<Card>) mCards.clone();
        mCards.clear();
        return other;
    }

    /**
     * Помещает карту в стопку
     * @param card карта, помещаемая в стопку
     */
    public void push(Card card) {

        if (card == null) {
            throw new NullPointerException("Cardpile excepion, method push");
        }

        if(mCards.contains(card)){
            return;
        }
        int cardPos = getIndexByOrder(card);
        if(cardPos >= 0){
            mCards.add(cardPos, card);
        }
        else {
            mCards.add(card);
        }
    }

    /**
     * Помещает в стопку некоторое количество карт
     * @param cards карты, помещаемые в стопку
     */
    public void push(List<Card> cards) {

        if(cards.isEmpty() || cards == null){
            throw new NullPointerException("Cardpile excepion, method push");
        }
        for (Card card : cards) {
            if(contains(card)){
                return;
            }
            push(card);
        }
    }

    /**
     * Получение индекса карты в стопке в соответствии с порядком сортировки карт
     * @param card карта
     * @return индекс карты в колоде
     */
    protected int getIndexByOrder(Card card){
        return -1;
    }

    /** Определяет наличие в стопке карты */
    public boolean contains(Card card) {

        for (Card tmpCard : mCards) {
           if(tmpCard.getFace() == card.getFace() && tmpCard.getSuit() == card.getSuit()){
                return true;
           }
        }
        
        return false;
    }

    /** Очищает стопку */
    public void clear() {
        mCards.clear();
    }

    /** Возвращает признак пустоты стопки */
    public boolean isEmpty(){
        return (mCards.isEmpty());
    }

    /** Итератор на стопку карт*/
    public Iterator<Card> iterator() {
        return mCards.iterator();
    }

    /** Преобразует стопку в строку */
    public String toString(){
        
        String result = "";
        for (Card tmpCard : mCards) {
            result += tmpCard.toString() + " ";
        }
        return result;
    }

    /**
     * Выбрать карту, если она не выбрана, и снять выбор в противном случае.
     * @param card карта, состояние которой требуется сменить.
     * @return true, если карта была выбрана, иначе false
     */
    public boolean switchCard(Card card) {

        //Проверяем, есть ли карта в списке выбранных уже, если есть - убираем её оттуда
        for (Card tmpCard : mSelectedCards) {
            if(tmpCard.getFace() == card.getFace() && tmpCard.getSuit() == card.getSuit()){
                mSelectedCards.remove(tmpCard);
                fireCardDeselectedEvent(tmpCard);
                return false;
            }
        }
        //Проверяем, есть ли в данной стопке такая карта, если есть, помещаем её в список выбранных
        if(contains(card)){
            mSelectedCards.add(card);
            fireCardSelectedEvent(card);
            return true;
        }
        return false;
    }

    /**
     * Количество выбранных карт в стопке.
     * @return число выбранных карт в стопке.
     */
    public int selectedCardsCount() {
        return mSelectedCards.size();
    }

    /**
     * Сбрасывает выделение в выбранных карт
    */
    public void removeSelectedCards() {
       for(Card card: mSelectedCards){
           mCards.remove(card);
       }
       mSelectedCards.clear();
    }

    /**
     * Возвращает список выбранных карт
     * @return выбранные карты
     */
    public ArrayList<Card> selectedCards(){
        return mSelectedCards;
    }

    /** Испустить событие Выбрана карта.*/
    protected void fireCardSelectedEvent(Card card) {
        GameEvent event = GameEvent.cardSwitched(this, card);
        Object[] listeners = listenerList.getListeners(GameEventListener.class);
        for (int i = 0; i < listeners.length; i++) {
            ((GameEventListener)listeners[i]).cardSelected(event);
        }
    }

    /** Испустить событие Снят выбор с карты.*/
    protected void fireCardDeselectedEvent(Card card) {
        GameEvent event = GameEvent.cardSwitched(this, card);
        Object[] listeners = listenerList.getListeners(GameEventListener.class);
        for (int i = 0; i < listeners.length; i++) {
            ((GameEventListener)listeners[i]).cardDeselected(event);
        }
    }

    /**
     * Добавить слушателя событий.
     * @param l добавляемый слушатель событий
     */
    public void addGameEventListener(GameEventListener l) {
        listenerList.add(GameEventListener.class, l);
    }

    /**
     * Удаление слушателя событий.
     * @param l слушатель, которого нужно забыть
     */
    public void removeGameEventListener(GameEventListener l) {
        listenerList.remove(GameEventListener.class, l);
    }
}
